﻿using DotNetCommon;
using DotNetCommon.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;

namespace DBUtil.Provider.Oracle;

#region oracle 表名规则总结
//参照: https://blog.csdn.net/u010476739/article/details/152324976
// 总结: oracle 仅支持 ""
// 并且 "" 内部不能再有双引号, 也不能有换行符, 而 \r\n\t 在oracle看来也是普通的六个字符
// 另外: oracle 表名最多有三级: "schema"."table"@dblink, 且最后一个 dblink 不能用双引号括起来
//示例:
// create table "test"(id int) -- 正常 test
// 
// create table "te\r\nst"(id int) -- te\r\nst
// create table "te
// st"(id int) 					   -- error 不能有换行符
// 
// CREATE TABLE "te""st123" (id INT); -- error 即使两个双引号也不行
#endregion

public partial class OracleAccess : DBAccess
{
    #region 表名或标识符分析/解析
    private static readonly IReadOnlyList<string> _QuotedCharacters = ["\"\""];
    /// <inheritdoc />
    public override IReadOnlyList<string> QuotedCharacters => _QuotedCharacters;
    /// <inheritdoc />
    public override string AddQuote(string pureName)
    {
        //oracle 不支持表名里有双引号
        if (pureName.IsNullOrEmpty() || pureName.TrimStart().StartsWith('"')) return pureName;
        return $"\"{pureName}\"";
    }
    /// <inheritdoc />
    public override IEnumerable<string> AnalysisQuotedName(string quotedName)
    {
        var len = quotedName?.Length ?? 0;
        if (len == 0) yield break;

        var currentIdx = 0;

        void readWhiteSpaceOrDot()
        {
            while (currentIdx < len && (quotedName[currentIdx] == '.' || char.IsWhiteSpace(quotedName[currentIdx]))) currentIdx++;
        }
        void readWhiteSpace()
        {
            while (currentIdx < len && char.IsWhiteSpace(quotedName[currentIdx])) currentIdx++;
        }
        string readQuotedSeg()
        {
            var startIdx = currentIdx;

            while (currentIdx < len)
            {
                if (quotedName[currentIdx] == '"')
                {
                    //找到了结束符号 oracle 不支持两个双引号合并成一个
                    return quotedName[startIdx..currentIdx];
                }

                currentIdx++;
            }

            throw new Exception($"对象名格式错误(结尾处缺少符号: 【\"】): {quotedName}");
        }

        string readUnQuotedSeg()
        {
            var startIdx = currentIdx;
            while (currentIdx < len)
            {
                if (quotedName[currentIdx] == '.' || quotedName[currentIdx] == '@' || char.IsWhiteSpace(quotedName[currentIdx]))
                {
                    return quotedName[startIdx..currentIdx];
                }
                currentIdx++;
            }
            return quotedName[startIdx..];
        }

        //消耗左侧空白及dot(.)
        readWhiteSpaceOrDot();
        if (currentIdx >= len) yield break;

        //读取第一次
        var curChar = quotedName[currentIdx];
        if (curChar == '"')
        {
            //有引用符
            currentIdx++;
            yield return readQuotedSeg();
            currentIdx++;//读取停止的时候指针一定对着最后一个 " 所以要跳一个
        }
        else
        {
            //没有引用符
            yield return readUnQuotedSeg();//读取停止的时候指针一定对着 . 或空白符
        }

        readWhiteSpaceOrDot();//读取停止的时候指针一定不会对着空白符或 .
        if (currentIdx >= len) yield break;

        //读取第二次
        curChar = quotedName[currentIdx];
        if (curChar == '"')
        {
            //有引用符
            currentIdx++;
            yield return readQuotedSeg();
            currentIdx++;//读取停止的时候指针一定对着最后一个 " 所以要跳一个
        }
        else
        {
            //没有引用符
            if (curChar == '@')
            {
                //读取到的是最后一个 dblink
                yield return "@";
                currentIdx++;
                readWhiteSpace();
                yield return readUnQuotedSeg();
                yield break;
            }
            yield return readUnQuotedSeg();//读取停止的时候指针一定对着 . 或空白符
        }

        readWhiteSpaceOrDot();//读取停止的时候指针一定不会对着空白符或 .
        if (currentIdx >= len) yield break;

        //读取第三次 只可能是 dblink
        curChar = quotedName[currentIdx];
        if (curChar == '@')
        {
            //读取到的是最后一个 dblink
            yield return "@";
            currentIdx++;
            readWhiteSpace();
            yield return readUnQuotedSeg();
            yield break;
        }
    }
    /// <inheritdoc />
    public override TableName ParseQuotedName(string quotedName)
    {
        if (quotedName.IsNullOrWhiteSpace()) return null;
        //基础解析
        var segArr = AnalysisQuotedName(quotedName).ToArray();
        if (segArr.IsNullOrEmpty()) throw new Exception($"解析对象名称失败: {quotedName}");

        if (segArr.Length == 1)
        {
            var name = segArr[0];
            AssertUtil.NotNullOrWhiteSpace(name);
            return new TableName(EnumDBType.Oracle, name);
        }
        else if (segArr.Length == 2)
        {
            var schemaName = segArr[0];
            var name = segArr[1];
            AssertUtil.NotNullOrWhiteSpace(name);
            AssertUtil.NotNullOrWhiteSpace(schemaName);
            return new TableName(EnumDBType.Oracle, name, schemaName: schemaName);
        }
        else if (segArr.Length == 3)
        {
            var name = segArr[0];
            var dblink = segArr[2];
            AssertUtil.NotNullOrWhiteSpace(name);
            AssertUtil.NotNullOrWhiteSpace(dblink);
            return new TableName(EnumDBType.Oracle, name, dblinkName: dblink);
        }
        else if (segArr.Length == 4)
        {
            var schemaName = segArr[0];
            var name = segArr[1];
            var dblink = segArr[3];
            AssertUtil.NotNullOrWhiteSpace(schemaName);
            AssertUtil.NotNullOrWhiteSpace(name);
            AssertUtil.NotNullOrWhiteSpace(dblink);
            return new TableName(EnumDBType.Oracle, name, schemaName: schemaName, dblinkName: dblink);
        }
        throw new Exception($"解析对象名称失败(oracle对象名称最多支持 schema.table@dblink 三段式): {quotedName}");
    }
    /// <inheritdoc />
    public override TableName CreateTableName()
    {
        return new TableName(EnumDBType.Oracle);
    }
    #endregion
}
